package com.rrd.hive.udf;

import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.hive.serde2.io.DoubleWritable;

/**
 * 按excel的rate函数计算利率
 * @author liubaoxin
 *
 */
public class ExcelRateUDF extends UDF {

	public DoubleWritable evaluate(double nper, double pmt, double pv) {
		return evaluate(nper,pmt,pv,0d,0d,0.1d);
	}
	public DoubleWritable evaluate(int nper, HiveDecimal pmt, HiveDecimal pv) {
		return evaluate(Double.valueOf(nper),pmt.doubleValue(),pv.doubleValue(),0d,0d,0.1d);
	}
    public DoubleWritable evaluate(double nper, double pmt, double pv, double fv, double type, double guess) {
    	DoubleWritable rateWritable = new DoubleWritable();
      //FROM MS http://office.microsoft.com/en-us/excel-help/rate-HP005209232.aspx
        int FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128
        double FINANCIAL_PRECISION = 0.0000001;//1.0e-8

        double y, y0, y1, x0, x1 = 0, f = 0, i = 0;
        double rate = guess;
        if (Math.abs(rate) < FINANCIAL_PRECISION) {
            y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
        } else {
            f = Math.exp(nper * Math.log(1 + rate));
            y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
        }
        y0 = pv + pmt * nper + fv;
        y1 = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;

        // find root by Newton secant method
        i = x0 = 0.0;
        x1 = rate;
        while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) {
            rate = (y1 * x0 - y0 * x1) / (y1 - y0);
            x0 = x1;
            x1 = rate;

            if (Math.abs(rate) < FINANCIAL_PRECISION) {
                y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
            } else {
                f = Math.exp(nper * Math.log(1 + rate));
                y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
            }

            y0 = y1;
            y1 = y;
            ++i;
        }
        rateWritable.set(rate);
        return rateWritable;

    }
public static void main(String[] args) {
	
	double nper=48d; 
	double pmt=2617.98;
	double pv=-60000;
	double fv=0d;
	double type=0d;
	double guess=0.1d;
	int FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128
    double FINANCIAL_PRECISION = 0.0000001;//1.0e-8

    double y, y0, y1, x0, x1 = 0, f = 0, i = 0;
    double rate = guess;
    if (Math.abs(rate) < FINANCIAL_PRECISION) {
        y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
    } else {
        f = Math.exp(nper * Math.log(1 + rate));
        y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
    }
    y0 = pv + pmt * nper + fv;
    y1 = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;

    // find root by Newton secant method
    i = x0 = 0.0;
    x1 = rate;
    while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) {
        rate = (y1 * x0 - y0 * x1) / (y1 - y0);
        x0 = x1;
        x1 = rate;

        if (Math.abs(rate) < FINANCIAL_PRECISION) {
            y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
        } else {
            f = Math.exp(nper * Math.log(1 + rate));
            y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
        }

        y0 = y1;
        y1 = y;
        ++i;
    }
    System.out.println(rate);
    
}
    

}
